from selenium.webdriver.common.by import By
import yaml
from typing import Tuple, Optional


class LocatorLoader:
    """元素定位符加载器，负责从配置文件解析定位信息"""

    def __init__(self, config_path: str = "location.yaml"):
        """
        初始化加载器并加载配置文件
        :param config_path: 定位符配置文件路径
        """
        self.config_path = config_path
        self.loc_config = self._load_config()

        # 支持的定位方式映射表（扩展时只需添加新的映射）
        self.supported_bys = {
            "ID": By.ID,
            "XPATH": By.XPATH,
            "CSS_SELECTOR": By.CSS_SELECTOR,
            "CLASS_NAME": By.CLASS_NAME,
            "TAG_NAME": By.TAG_NAME,
            "LINK_TEXT": By.LINK_TEXT,
            "PARTIAL_LINK_TEXT": By.PARTIAL_LINK_TEXT,
            "NAME": By.NAME
        }

    def _load_config(self) -> dict:
        """加载并解析 YAML 配置文件"""
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                return yaml.safe_load(f) or {}  # 处理空文件
        except FileNotFoundError:
            raise FileNotFoundError(f"配置文件不存在: {self.config_path}")
        except yaml.YAMLError as e:
            raise ValueError(f"配置文件格式错误: {str(e)}")

    def get_locator(self, config_key: str, locator_key: str) -> Tuple[str, str]:
        """
        获取元素定位符（By类型 + 定位表达式）
        :param config_key: 配置分组键（如 "auth"）
        :param locator_key: 定位符键（如 "username_loc"）
        :return: (By常量, 定位表达式)
        """
        # 1. 从配置中获取定位信息
        group_config = self.loc_config.get(config_key)
        if not group_config:
            raise KeyError(f"配置分组不存在: {config_key}")

        locator_info = group_config.get(locator_key)
        if not locator_info:
            raise KeyError(f"定位符不存在: {config_key}.{locator_key}")

        # 2. 校验定位信息格式
        if not isinstance(locator_info, dict) or "by" not in locator_info or "value" not in locator_info:
            raise ValueError(f"定位符配置格式错误: {config_key}.{locator_key}，需包含 'by' 和 'value'")

        # 3. 转换定位方式为 By 常量
        by_type = locator_info["by"].upper()  # 统一转为大写，兼容大小写输入
        if by_type not in self.supported_bys:
            raise ValueError(f"不支持的定位方式: {by_type}，支持的方式: {list(self.supported_bys.keys())}")

        return self.supported_bys[by_type], locator_info["value"]

    def reload_config(self) -> None:
        """重新加载配置文件（用于动态更新配置时）"""
        self.loc_config = self._load_config()
